﻿using FriendFunny.Auth.Jwt.Option;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;

namespace FriendFunny.Auth.Jwt
{
    public class JwtHelper
    {
        /// <summary>
        /// 生成 Token
        /// </summary>
        /// <param name="issuerSigningKey"></param>
        /// <param name="payload"></param>
        /// <param name="algorithm"></param>
        /// <returns></returns>
        public static string Encrypt(Dictionary<string, object> payload,long? expiredTime = null, string algorithm = SecurityAlgorithms.HmacSha256)
        {
            // 装载公共负载
            var (Payload, JWTSettings) = CombinePayload(payload, expiredTime);

            var stringPayload = JsonSerializer.Serialize(Payload);
            SigningCredentials? credentials = null;

            if (!string.IsNullOrWhiteSpace(JWTSettings.IssuerSigningKey))
            {
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes((JWTSettings.IssuerSigningKey)));
                credentials = new SigningCredentials(securityKey, algorithm);
            }

            var tokenHandler = new JsonWebTokenHandler();
            return credentials == null ? tokenHandler.CreateToken(stringPayload) : tokenHandler.CreateToken(stringPayload, credentials);
        }

        /// <summary>
        /// 创建公共 Payload（负载信息）
        /// </summary>
        /// <param name="payload">https://jwt.io/introduction</param>
        /// <param name="expiredTime">过期时间，单位：分钟</param>
        /// <returns></returns>
        private static (IDictionary<string, object> Payload, JwtOptions JWTSettings) CombinePayload(IDictionary<string, object> payload, long? expiredTime = null)
        {
            var jwtSettings = GetJWTAppSettings();
            var datetimeOffset = DateTimeOffset.UtcNow;

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iat))
            {
                payload.Add(JwtRegisteredClaimNames.Iat, datetimeOffset.ToUnixTimeSeconds());
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Nbf))
            {
                payload.Add(JwtRegisteredClaimNames.Nbf, datetimeOffset.ToUnixTimeSeconds());
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Exp))
            {
                var minute = expiredTime ??  20;
                payload.Add(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddMinutes(minute).ToUnixTimeSeconds());
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iss))
            {
                payload.Add(JwtRegisteredClaimNames.Iss, jwtSettings?.ValidIssuer ?? "");
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Aud))
            {
                payload.Add(JwtRegisteredClaimNames.Aud, jwtSettings?.ValidAudience ?? "");
            }

            return (payload, jwtSettings);
        }

        /// <summary>
        /// 获取 JWT 配置
        /// </summary>
        /// <returns></returns>
        public static JwtOptions GetJWTAppSettings()
        {
            var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            var builder = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile(string.IsNullOrWhiteSpace(env) ? "appsettings.json" : $"appsettings.{env}.json", true, reloadOnChange: true);

            var config = builder.Build();

            var jwtSettings = new JwtOptions();
            config.GetSection("JWTSettings").Bind(jwtSettings);

            return jwtSettings;
        }


        /// <summary>
        /// 生成refresh_Token
        /// </summary>
        /// <returns></returns>
        public static string CreateRefreshToken()
        {
            var refresh = new byte[32];
            using (var number = RandomNumberGenerator.Create())
            {
                number.GetBytes(refresh);
                return Convert.ToBase64String(refresh);
            }
        }
    }
}
